KeyedCollection

Turns the inheriting class into a keyed collection. The key is based on the singular class' clustered index. The requirements (except for dup) are taken care of when you include the keyeditem in the T class.

T should represent a single row in the database. Use this when T has foreign keys.

mixin template KeyedCollection (
T
) if (
usableForKeyedCollection!(T)
) {}

Constructors

this
this(T item)
this(I items)
this()

Initializes this. Adds item to this and connects to the signals emitted by item.

Members

Aliases

collectionof
alias collectionof = T

Alias letting you know what this is a collection of.

key_type
alias key_type = typeof(T.key)

The key_type is alias'd as the type since it looked better than having typeof(T.key) everywhere.

Functions

add
void add(T item, Flag!"notifyChange" notifyChange)
void add(I items, Flag!"notifyChange" notifyChange)

Adds item to this and connects to the signals emitted by item. Notifies that the length of this has changed.

contains
bool contains(T item)
bool contains(key_type clIdx)
bool contains(A a)

Checks if item is in the collection.

length
size_t length()

Gets the length of the collection.

markAsSaved
void markAsSaved()

Changes this to not contain changes and also marks all the items as saved. Should only be used after a save.

notify
void notify(string propertyName, key_type item_key)

Notifies this which property changed and sets containsChanges to true. This also emits a signal with the property name that changed and the key to it in this collection.

opApply
int opApply(int delegate(ref T) dg)
int opApply(int delegate(key_type, ref T) dg)

Allows you to use this in a foreach loop.

opBinaryRight
inout(T)* opBinaryRight(T item)
inout(T)* opBinaryRight(key_type clIdx)
inout(T)* opBinaryRight(A a)

The InExpression yields a pointer to the value if the key is in the associative array, or null if not.

opDispatch
auto opDispatch(A a)

Forwards all methods not specified by this abstract class to the private associative array.

opIndex
inout(T) opIndex(T item)
inout(T) opIndex(key_type clIdx)
inout(T) opIndex(A a)

Gets the approriate T. You can either use an item that equals the item you want back, a key of the item you want back or parameters that can make the key for the item you want back.

opOpAssign
auto ref opOpAssign(T item)
auto ref opOpAssign(I items)

This just calls $(SRCTAG KeyedCollection.add).

remove
void remove(key_type item_key, Flag!"notifyChange" notifyChange)
void remove(T item)
void remove(A a)

Removes an item from this and disconnects the signals. Notifies that the length of this has changed by emitting "remove".

violatesExclusionConstraints
bool violatesExclusionConstraints(T item)

Checks if the item has any conflicting exclusion constraints.

violatesExclusionConstraints
bool violatesExclusionConstraints(T item, string constraintName)

Checks if the item has any conflicting exclusion constraints.

violatesUniqueConstraints
bool violatesUniqueConstraints(T item, string constraintName)
bool violatesUniqueConstraints(T item)

Checks if the item has any conflicting unique constraints. This is more extensive than $(SRCTAG KeyedCollection.contains).

Mixins

collectionChanged
mixin Signal!(string, key_type) collectionChanged

The signal used to emit changes that occur in this.

Properties

containsChanges
bool containsChanges [@property getter]

Read-only property telling if this contains changes.

enforceConstraints
ubyte enforceConstraints [@property setter]

Write-only property to enforce the constraints. By default this is (Enforce.check | Enforce.unique | Enforce.foreignKey | Enforce.exclusion) but you may set it to 0 if you have a lot of initial data and already trust that it does not violate any constraints.

Variables

_items
T[key_type] _items;
Undocumented in source.

Examples

1 // singular class this holds all of the columns
2 class Candy
3 {
4 private:
5     string _name;
6     int _ranking;
7 public:
8     // marking name as part of the primary key
9     @PrimaryKeyColumn @NotNull
10     @property string name() const nothrow pure @safe @nogc
11     {
12         return _name;
13     }
14     @property void name(string value)
15     {
16         setter(_name, value);
17     }
18     @property int ranking() const nothrow pure @safe @nogc
19     {
20         return _ranking;
21     }
22     // making sure that ranking will always be above 0
23     @CheckConstraint!(a => a > 0, "chk_Candys_ranking")
24     @property void ranking(int value)
25     {
26         setter(_ranking, value);
27     }
28 
29     this(string name, int ranking)
30     {
31         this._name = name;
32         this._ranking = ranking;
33         // need to initialize the keyed item
34         initializeKeyedItem();
35     }
36     Candy dup() const
37     {
38         return new Candy(this._name, this._ranking);
39     }
40     // the default is to make the primary key into the clustered index
41     // which allows you to search based on the primary key
42     mixin KeyedItem!();
43 }
44 
45 // plural class
46 // I am using an alias since BaseKeyedCollection
47 // takes care of everything I want to do for this example in one line.
48 alias Candies = BaseKeyedCollection!(Candy);
49 
50 // Candies is a collection of Candy
51 static assert(is(Candies.collectionof == Candy));
52 
53 // source:
54 // http://www.bloomberg.com/ss/09/10/1021_americas_25_top_selling_candies/
55 // should be Milky not Milkey, this is wrong on purpose
56 auto milkyWay = new Candy("Milkey Way", 18);
57 auto snickers = new Candy("Snickers", 4);
58 auto reesesPBCups = new Candy("Reese's Peanut Butter Cups", 2);
59 
60 auto mars = new Candies([milkyWay, snickers]);
61 assert(mars.length == 2);
62 assert(!mars.containsChanges);
63 
64 auto hershey = new Candies(reesesPBCups);
65 assert(hershey.length == 1);
66 
67 // use the class as an index and confirm it returns the correct value
68 assert(mars[milkyWay] is milkyWay);
69 // use the primary key as an index
70 auto pk = Candy.PrimaryKey("Milkey Way");
71 assert(pk.name == milkyWay.name);
72 assert(mars[pk] is milkyWay);
73 // use the contents of the primary key as an index
74 assert(mars["Milkey Way"] is milkyWay);
75 
76 // milky way is in mars
77 assert(mars.contains(pk));
78 // reesesPBCups is not in mars
79 assert(!mars.contains(reesesPBCups));
80 
81 // now we change the name to be correct
82 mars[pk].name = "Milky Way"; // remember pk is primary key for milky way
83 
84 // since we changed milky way's name, mars contains changes
85 assert(mars.containsChanges);
86 
87 // since we had name in pk spelled incorrectly
88 // and changed it, the primary key in mars has
89 // updated so Milkey Way is no longer in it but
90 // Milky Way is.
91 assert(!mars.contains("Milkey Way"));
92 assert(mars.contains("Milky Way"));
93 
94 // looping over mars we make sure the key can be used to get
95 // the correct value.
96 foreach(name_pk, candy; mars)
97 {
98     assert(mars[name_pk] == candy);
99 }
100 
101 // trying to add another candy with the same name will
102 // result in a unique constraint violation even if the ranking is different
103 auto milkyWay2 = new Candy("Milky Way", 16);
104 assert(milkyWay.name == milkyWay2.name);
105 assert(milkyWay.ranking != milkyWay2.ranking);
106 import std.exception : assertThrown;
107 assertThrown!(UniqueConstraintException)(mars ~= milkyWay2);
108 
109 // ranking has a check constraint saying ranking always must be greater
110 // than 0. setting it to -1 resolves in a CheckConstraintException.
111 assertThrown!(CheckConstraintException)(mars["Milky Way"].ranking = -1);
112 // Since name is part of the primary key we must mark it with NotNull
113 // trying to set this to null will result in a CheckConstraintException.
114 assertThrown!(CheckConstraintException)(mars["Milky Way"].name = null);
115 
116 // violatesUniqueConstraints will tell you which unique constraint
117 // is violated if any
118 string violatedConstraint;
119 assert(mars.violatesUniqueConstraints(milkyWay2, violatedConstraint));
120 assert(violatedConstraint !is null && violatedConstraint == "PrimaryKey");
121 
122 // removing milky way from mars
123 mars.remove(milkyWay);
124 // this means milkyWay2 is no longer a duplicate
125 assert(!mars.violatesUniqueConstraints(milkyWay2, violatedConstraint));
126 assert(violatedConstraint is null);

Meta